﻿using Sidvall.Security;
using Sidvall.Serialization;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Routing;

namespace Sidvall.Web
{
    public class ControllerManager
    {
        #region Public Members

        #region AuthorizationManager

        private Sidvall.Security.AuthorizationManager _AuthorizationManager;
        public virtual Sidvall.Security.AuthorizationManager AuthorizationManager
        {
            get
            {
                if (_AuthorizationManager != null)
                    return _AuthorizationManager;
                return Sidvall.SystemContext.Current.AuthorizationManager;
            }
            set
            {
                _AuthorizationManager = value;
            }
        }

        #endregion
        #region LogManager

        private Sidvall.Logging.LogManager _LogManager;
        public virtual Sidvall.Logging.LogManager LogManager
        {
            get
            {
                if (_LogManager != null)
                    return _LogManager;
                return Sidvall.SystemContext.Current.LogManager;
            }
            set
            {
                _LogManager = value;
            }
        }

        #endregion

        #region EnsureIsAuthorized

        public AuthorizeResult EnsureIsAuthorized(string resource, string action)
        {
            return this.AuthorizationManager.EnsureIsAuthorized(resource, action);
        }
        public AuthorizeResult EnsureIsAuthorized(IEnumerable<string> resources, string action)
        {
            return this.AuthorizationManager.EnsureIsAuthorized(resources, action);
        }

        #endregion
        #region EnsureIsAuthorizedAsync

        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(ApiController controller, Sidvall.Business.IDataPortalContext context)
        {
            return await this.AuthorizationManager.EnsureIsAuthorizedAsync(context);
        }

        public async Task<AuthorizeResult> EnsureIsAuthorizedAsync(System.Web.Mvc.Controller controller, Sidvall.Business.IDataPortalContext context)
        {
            return await this.AuthorizationManager.EnsureIsAuthorizedAsync(context);
        }

        #endregion
        #region GetResponseMessage

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        public HttpResponseMessage GetResponseMessage(ApiController controller, System.Exception ex)
        {
            HttpResponseMessage message;

            if (controller == null)
                return null;
            if (ex == null)
                return null;
            var unauthenticatedException = ex.GetBaseException() as Sidvall.Security.UnauthenticatedException;
            if (unauthenticatedException != null)
            {
                message = controller.Request.CreateResponse(HttpStatusCode.InternalServerError, Sidvall.Security.UnauthenticatedExceptionContext.CreateContext(unauthenticatedException));
                message.ReasonPhrase = Sidvall.Net.ReasonPhrases.UnauthenticatedExceptionReasonPhrase;
                return message;
            }
            var unauthorizedException = ex.GetBaseException() as Sidvall.Security.UnauthorizedException;
            if (unauthorizedException != null)
            {
                message = controller.Request.CreateResponse(HttpStatusCode.Unauthorized, Sidvall.Security.UnauthorizedExceptionContext.CreateContext(unauthorizedException));
                return message;
            }
            var businessException = ex.GetBaseException() as Sidvall.Business.BusinessException;
            if (businessException != null)
            {
                message = controller.Request.CreateResponse(HttpStatusCode.InternalServerError, Sidvall.Business.BusinessExceptionContext.CreateContext(businessException));
                message.ReasonPhrase = Sidvall.Net.ReasonPhrases.BusinessExceptionReasonPhrase;
                return message;
            }
            message = controller.Request.CreateResponse(HttpStatusCode.InternalServerError);
            message.ReasonPhrase = ex.Message.Replace("\n", "").Replace("\r", "");
            return message;
        }

        #endregion
        #region GetResourceItems

        public Sidvall.Security.ResourceItemCollection GetResourceItems(IEnumerable<IHttpRoute> routes)
        {
            var routeTemplates = GetRouteTemplates(routes);
            return GetResourceItems(routeTemplates);
        }
        public Sidvall.Security.ResourceItemCollection GetResourceItems(IEnumerable<IHttpRoute> routes, RouteTemplateSettings settings)
        {
            var routeTemplates = GetRouteTemplates(routes);
            return GetResourceItems(routeTemplates, settings);
        }
        public Sidvall.Security.ResourceItemCollection GetResourceItems(IEnumerable<string> routeTemplates)
        {
            return GetResourceItems(routeTemplates, null);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.Security.ResourceItemCollection GetResourceItems(IEnumerable<string> routeTemplates, RouteTemplateSettings settings)
        {
            string path;

            if (settings == null)
                settings = RouteTemplateSettings.GetDefaultSettings(true);
            var items = new Sidvall.Security.ResourceItemCollection();
            if (routeTemplates != null)
            {
                foreach (var routeTemplate in routeTemplates)
                {
                    if ((!string.IsNullOrWhiteSpace(settings.RootPath)) && (routeTemplate.StartsWith(settings.RootPath, System.StringComparison.OrdinalIgnoreCase)))
                    {
                        path = routeTemplate.Substring(settings.RootPath.Length);
                    }
                    else
                    {
                        path = routeTemplate;
                    }
                    var item = settings.GetResourceActionItem(path);
                    if ((item?.Resources != null) && (item?.Actions != null))
                    {
                        foreach (var resourceItem in item.Resources)
                        {
                            foreach (var actionItem in item.Actions)
                                items.Add(resourceItem, actionItem, null, routeTemplate);
                        }
                    }
                }
            }
            return items;
        }

        #endregion
        #region GetRouteTemplates

        public System.Collections.ObjectModel.Collection<string> GetRouteTemplates(IHttpRoute route)
        {
            var routeTemplates = new System.Collections.ObjectModel.Collection<string>();
            LoadRouteTemplates(route, routeTemplates);
            return routeTemplates;
        }
        public System.Collections.ObjectModel.Collection<string> GetRouteTemplates(IEnumerable<IHttpRoute> routes)
        {
            var routeTemplates = new System.Collections.ObjectModel.Collection<string>();
            LoadRouteTemplates(routes, routeTemplates);
            return routeTemplates;
        }

        #endregion
        #region IsAuthorized

        public bool IsAuthorized(string resource, string action)
        {
            return this.AuthorizationManager.IsAuthorized(resource, action);
        }
        public bool IsAuthorized(IEnumerable<string> resources, string action)
        {
            return this.AuthorizationManager.IsAuthorized(resources, action);
        }

        #endregion
        #region IsAuthorizedAsync

        public async Task<bool> IsAuthorizedAsync(ApiController controller, Sidvall.Business.IDataPortalContext context)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(context);
        }
        public async Task<bool> IsAuthorizedAsync(ApiController controller, string resource, string action, string subAction)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(resource, action, subAction);
        }
        public async Task<bool> IsAuthorizedAsync(ApiController controller, IEnumerable<string> resources, string action, string subAction)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(resources, action, subAction);
        }
        public async Task<bool> IsAuthorizedAsync(ApiController controller, IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(resources, actions, subActions);
        }
        public async Task<bool> IsAuthorizedAsync(ApiController controller, string resource, string action, string subAction, string resourceItemId)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(resource, action, subAction, resourceItemId);
        }

        public async Task<bool> IsAuthorizedAsync(System.Web.Mvc.Controller controller, Sidvall.Business.IDataPortalContext context)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(context);
        }
        public async Task<bool> IsAuthorizedAsync(System.Web.Mvc.Controller controller, string resource, string action, string subAction)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(resource, action, subAction);
        }
        public async Task<bool> IsAuthorizedAsync(System.Web.Mvc.Controller controller, IEnumerable<string> resources, string action, string subAction)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(resources, action, subAction);
        }
        public async Task<bool> IsAuthorizedAsync(System.Web.Mvc.Controller controller, IEnumerable<string> resources, IEnumerable<string> actions, IEnumerable<string> subActions)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(resources, actions, subActions);
        }
        public async Task<bool> IsAuthorizedAsync(System.Web.Mvc.Controller controller, string resource, string action, string subAction, string resourceItemId)
        {
            return await this.AuthorizationManager.IsAuthorizedAsync(resource, action, subAction, resourceItemId);
        }

        #endregion
        #region LogAsync

        public async Task LogAsync(System.Exception ex)
        {
            if (ex == null)
                return;
            if (this.LogManager != null)
            {
                await this.LogManager.WriteAsync(ex, Sidvall.Logging.LogCategories.WebApi);
            }
        }
        public async Task LogAsync(Sidvall.Business.IDataPortalContext context)
        {
            if (context == null)
                return;
            if (this.LogManager == null)
                return;
            if (!this.LogManager.ShouldLog(new string[] { Sidvall.Logging.LogCategories.WebApi }, Logging.EventType.Information))
                return;

            var title = context.Resource.Append(context.Action, ".");
            var message = Sidvall.Serialization.SerializerManager.Current.SerializeObject(context);
            await this.LogManager.WriteAsync(title, message, new string[] { Sidvall.Logging.LogCategories.WebApi }, Logging.EventType.Information);
        }

        #endregion
        #region ToString

        public override string ToString()
        {
            return "ControllerManager - Sidvall.Web";
        }

        #endregion

        #endregion
        #region Private Members

        #region LoadRouteTemplates

        private void LoadRouteTemplates(IEnumerable<IHttpRoute> routes, System.Collections.ObjectModel.Collection<string> routeTemplates)
        {
            foreach (var route in routes)
                LoadRouteTemplates(route, routeTemplates);
        }
        private void LoadRouteTemplates(IHttpRoute route, System.Collections.ObjectModel.Collection<string> routeTemplates)
        {
            var childRoutes = route as IEnumerable<IHttpRoute>;
            if (childRoutes != null)
                LoadRouteTemplates(childRoutes, routeTemplates);
            else
                routeTemplates.Add(route.RouteTemplate);
        }

        #endregion

        #endregion
        #region Constructors

        public ControllerManager()
        {
        }

        #endregion
    }
}
